<?php

namespace app\queue\redis;

use constant\RedisConstant;
use util\RedisTool;
use util\WorkWxTool;
use Webman\RedisQueue\Consumer;
use Webman\RedisQueue\Client;

class AssignTaskListQueue implements Consumer
{
    public $queue = RedisConstant::AssignTaskList;

    //消费
    public function consume($data)
    {
        $taskType = $data['taskType']; //任务类型、标识
        $taskSign = $data['taskSign']; //分派名称、标识、ID
        $info = $data['info']; //派发的数据
        $receiveInfo = $data['receiveInfo'] ?? []; //指定领取人信息
        $allocationMode = $data['allocationMode'] ?? 0; //上次的分配模式
        $redistribution = $data['redistribution'] ?? false; //是否需要重新分配
        $lastUserInfo = $data['lastUserInfo'] ?? []; //重新分配的前置用户
        //派发配置
        $taskSetting = RedisConstant::TaskPrefix . $taskType . ':task:' . $taskSign;
        $setting = RedisTool::get_forever($taskSetting, 'config');
        $resultData = self::getAssignUserInfo($setting, $taskType, $taskSign, $info, $receiveInfo);
        /**
         * result: -3:线索已被领取，不处理
         * result: -2:门店不分配或者信息错误：直接发通知
         * result: -1:线索不存在：不处理
         * result: 0:睡眠时间：重新加入待分配列表
         * result: 1:成功事件，更新信息，发送通知，写记录，加入分配队列
         * result: 2:没有分配人，重新加入待分配列表，发送没有人的通知
         */
        switch ($resultData['result']) {
            case -3:
                //已被领取，不处理
                break;
            case -2:
                //门店不分配或者信息错误
                break;
            case -1:
                //不存在，不处理
                break;
            case 0:
                //处于休息时间，加入分配队列
                Client::send(RedisConstant::AssignTaskList, [
                    'taskType' => $taskType,
                    'taskSign' => $taskSign,
                    'info' => $info,
                    'receiveInfo' => $receiveInfo,
                ], $resultData['sendList']['sleepTime']);
                break;
            case 1:
                //成功获取分配人员
                //距离今天结束
                $todayEnd = strtotime(date('Y-m-d')) + 86400 - time();
                $normalReceiveKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskReceiveTimes . $taskSign . '_' . $resultData['userInfo']['user_id'];
                $normalKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskAssignNormal . $taskSign;
                $dutyReceiveKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskDutyTimes . $taskSign . '_' . $resultData['userInfo']['user_id'];
                $dutyKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskAssignDuty . $taskSign;
                $RegularReceiveKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskReceiveRegular . $taskSign . '_' . $resultData['userInfo']['user_id'];
                //根据分配模式处理分配队列
                //顺序分配
                if ($resultData['assign']['mode'] == 1) {
                    //更新当天分配次数
                    $times = RedisTool::get_time($normalReceiveKey, 'list') ?: 0;
                    RedisTool::set_time($normalReceiveKey, $times + 1, $todayEnd, 'list');
                    //判断是否置入分配队列
                    if ($resultData['userInfo']['receive_times'] == 0 || $times + 1 < $resultData['userInfo']['receive_times']) {
                        //置入顺序分配队列
                        RedisTool::set_rPush($normalKey, array_merge($resultData['userInfo'], ['assign_mode' => $resultData['assign']['mode'], 'in_assign' => 1]), 'list');
                    }
                }
                //指定分配
                if ($resultData['assign']['mode'] == 2) {
                    //更新当天分配次数
                    $times = RedisTool::get_time($normalReceiveKey, 'list');
                    RedisTool::set_time($normalReceiveKey, $times + 1, $todayEnd, 'list');
                    //置入顺序分配队列
                    RedisTool::set_rPush($normalKey, array_merge($resultData['userInfo'], ['assign_mode' => $resultData['assign']['mode'], 'in_assign' => 1]), 'list');
                }
                //比例分配
                if ($resultData['assign']['mode'] == 3) {
                    $times = RedisTool::get_time($normalReceiveKey, 'list');
                    RedisTool::set_time($normalReceiveKey, $times + 1, $todayEnd, 'list');
                    //剩余概率
                    $ratio = $resultData['userInfo']['weight'] ? bcdiv($resultData['userInfo']['weight'], ($times ?: 0) + 2, 2) : 0;
                    RedisTool::set_zAdd($normalKey, array_merge($resultData['userInfo'], ['assign_mode' => $resultData['assign']['mode'], 'in_assign' => 1]), $ratio, 'list');
                }
                //均等分配
                if ($resultData['assign']['mode'] == 4) {
                    $times = RedisTool::get_time($normalReceiveKey, 'list');
                    RedisTool::set_time($normalReceiveKey, $times + 1, $todayEnd, 'list');
                    //判断是否置入分配队列
                    if ($resultData['userInfo']['receive_times'] || $times + 1 < $resultData['userInfo']['receive_times']) {
                        //置入均等分配队列
                        RedisTool::zset_incr($normalKey, 1, array_merge($resultData['userInfo'], ['assign_mode' => $resultData['assign']['mode'], 'in_assign' => 1]), 'list');
                    }
                }
                //值班时间
                if ($resultData['assign']['mode'] == 8) {
                    //更新当天分配次数
                    $times = RedisTool::get_time($dutyReceiveKey, 'list');
                    RedisTool::set_time($dutyReceiveKey, $times + 1, $todayEnd, 'list');
                    //判断是否置入分配队列
                    if ($resultData['userInfo']['receive_times'] || $times + 1 < $resultData['userInfo']['receive_times']) {
                        //置入顺序分配队列
                        RedisTool::set_rPush($dutyKey, array_merge($resultData['userInfo'], ['assign_mode' => $resultData['assign']['mode'], 'in_assign' => 1]), 'list');
                    }
                }
                //固定第一个分配
                if ($resultData['assign']['mode'] == 10) {
                    RedisTool::set_time($RegularReceiveKey, 1, $todayEnd, 'list');
                }
                //是重新分配的处理上个用户次数
                if ($redistribution) {
                    if ($todayEnd > $resultData['assign']['skip_minute'] * 60) {
                        if ($allocationMode == 8) {
                            $lastReceiveKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskDutyTimes . $taskSign . '_' . $lastUserInfo['user_id'];
                            $lastKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskAssignDuty . $taskSign;
                            //更新当天分配次数
                            $times = RedisTool::get_time($lastReceiveKey, 'list');
                            RedisTool::set_time($lastReceiveKey, $times - 1, $todayEnd, 'list');
                            //检查用户的领取是否还在队列，没有在队列则更新队列
                            //如果分配次数等于已分配次数，则已经移除队列了，更新回队列
                            if ($lastUserInfo['duty_times'] != 0 && $lastUserInfo['duty_times'] == $times)
                                RedisTool::set_rPush($lastKey, array_merge($lastUserInfo, ['assign_mode' => $allocationMode, 'in_assign' => 1]), 'clue');
                        } else if ($allocationMode != 3) {
                            /**
                             * 排除比例分配的情况
                             * 重新计算比例以及次数之后会导致比例一直高，后续依旧优先分配
                             * 从而不能达到跳过的操作
                             */
                            $lastReceiveKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskReceiveTimes . $taskSign . '_' . $lastUserInfo['user_id'];
                            $lastKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskAssignNormal . $taskSign;
                            //更新当天分配次数
                            $times = RedisTool::get_time($lastReceiveKey, 'list');
                            RedisTool::set_time($lastReceiveKey, $times - 1, $todayEnd, 'list');
                            //检查用户的领取是否还在队列，没有在队列则更新队列
                            //如果分配次数等于已分配次数，则已经移除队列了，更新回队列
                            if ($lastUserInfo['receive_times'] != 0 && $lastUserInfo['receive_times'] == $times && $allocationMode != 3) {
                                if ($allocationMode == 4) {
                                    //置入均等分配队列
                                    RedisTool::zset_incr($lastKey, -1, array_merge($lastUserInfo, ['assign_mode' => $allocationMode, 'in_assign' => 1]), 'list');
                                } else {
                                    RedisTool::set_rPush($lastKey, array_merge($resultData['userInfo'], ['assign_mode' => $allocationMode, 'in_assign' => 1]), 'list');
                                }
                            }
//                            //如果需要处理比例，去掉注释以及if的判定
//                            if ($allocationMode == 3) {
//                                //剩余概率
//                                $ratio = $lastUserInfo['weight'] ? bcdiv($lastUserInfo['weight'], ($times ?: 0) + 1, 2) : 0;
//                                RedisTool::set_zAdd($lastKey, array_merge($lastUserInfo, ['allocation_mode' => $allocationMode, 'in_assign' => 1]), $ratio, 'list');
//                            }
                        }
                    }
                }
                //发送模版通知
                Client::send(RedisConstant::AssignNoticeSend, [
                    'taskType' => $taskType,
                    'taskSign' => $taskSign,
                    'info' => $info,
                    'userInfo' => $resultData['userInfo'],
                    'noticeType' => 1,
                ]);
                //如果需要线索跳过--最后再加入队列，避免重复队列
                if ($resultData['assign']['skip_minute'] > 0 && !in_array($resultData['assign']['mode'], [9, 10, 99])) {
                    //消息通知
                    Client::send(RedisConstant::AssignNoticeSend, [
                        'taskType' => $taskType,
                        'taskSign' => $taskSign,
                        'info' => $info,
                        'userInfo' => $resultData['userInfo'],
                        'noticeType' => 2,
                    ]);
                    //加入待分配队列
                    Client::send(RedisConstant::AssignTaskList, [
                        'taskType' => $taskType,
                        'taskSign' => $taskSign,
                        'info' => $info,
                        'receiveInfo' => $receiveInfo,
                        'allocationMode' => $resultData['assign']['mode'],
                        'redistribution' => true,
                        'lastUserInfo' => $resultData['userInfo'],
                    ], $resultData['assign']['skip_minute'] * 60);
                }
                break;
            case 2:
                //发送没有人的通知
                //消息通知
                Client::send(RedisConstant::AssignNoticeSend, [
                    'taskType' => $taskType,
                    'taskSign' => $taskSign,
                    'info' => $info,
                    'userInfo' => [],
                    'noticeType' => 3,
                ]);
                //加入待分配队列
                Client::send(RedisConstant::AssignTaskList, [
                    'taskType' => $taskType,
                    'taskSign' => $taskSign,
                    'info' => $data['info'],
                    'receiveInfo' => $receiveInfo,
                ], $resultData['sendList']['sleepTime']);
                break;
        }
    }

    // 消费失败回调
    public function onConsumeFailure(\Throwable $e, $package)
    {
        WorkWxTool::ErrorMsg('任务派发执行失败', $e->getMessage() . '\n' . json_encode($package['data'], 256));
    }

    public static function getAssignUserInfo($setting, $taskType, $taskSign, $info, $receiveInfo): array
    {
        $resultData = [];
        //派发的数据不存在,不存在ID
        if (!$info || !$info['id']) {
            $resultData['result'] = -1;
            return $resultData;
        }
        //派发的数据已被领取
        if (RedisTool::get_exists(RedisConstant::TaskPrefix . $taskType . ':task:' . $taskSign . ':' . RedisConstant::TaskReceiveStatus . $info['id'], 'list')) {
            RedisTool::del(RedisConstant::TaskPrefix . $taskType . ':task:' . $taskSign . ':' . RedisConstant::TaskReceiveStatus . $info['id'], 'list');
            $resultData['result'] = -3;
            return $resultData;
        }
        //是否允许派发
        if (!$setting || !$setting['allowAssign']) {
            $resultData['result'] = -2;
            return $resultData;
        }
        //是否处于派发时间
        $sleepTime = 0;
        if ($setting['assignTime']) {
            list($begin, $end) = explode('-', $setting['assignTime']);
            $begin = strtotime(date('Y-m-d') . ' ' . $begin);
            $end = strtotime(date('Y-m-d') . ' ' . $end);
            if ($begin >= $end) $end = $end + 86400; //结束时间小于开始时间算作明日时间
            if ((time() < $begin || time() > $end))
                $sleepTime = $begin - time() + (time() < $begin ? 0 : 86400); //分配时间 - 现在的时间 + 明日（86400）
        }
        if ($sleepTime) {
            //待分配线索添加分配延迟，避免打乱派发顺序
            $number = RedisTool::get_time(RedisConstant::TaskAssignNumber . $taskSign, 'list') ?? 0;
            RedisTool::set_time(RedisConstant::TaskAssignNumber . $taskSign, $number + 1, $sleepTime, 'list');
            $resultData['result'] = 0;
            $resultData['sendList'] = ['sleepTime' => $sleepTime + $number * 3];
            return $resultData;
        }
        //0 => '不分配', 1 => '循环分配', 2 => '指定分配', 3 => '比例分配', 4 => '均等分配', 8 => '值班分配', 9 => '指定用户',10 => 固定分配, 99 => '分配补充';
        //指定了人的分配
        if ($receiveInfo) {
            $resultData['result'] = 1;
            $resultData['userInfo'] = $receiveInfo;
            $resultData['assign'] = ['mode' => 9, 'skip_minute' => 0];
            return $resultData;
        }

        $redisKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskAssignNormal . $taskSign;
        $assignMode = $setting['assignMode'];
        //如果在值班时间 值班只按照顺序分配
        if ($setting['assignDuty'] && $setting['dutyTime']) {
            $inDuty = false;
            foreach ($setting['dutyTime'] as $dutyTime) {
                list($begin, $end) = explode('-', $dutyTime);
                $begin = strtotime(date('Y-m-d') . ' ' . $begin);
                $end = strtotime(date('Y-m-d') . ' ' . $end);
                if ($begin >= $end) $end = $end + 86400;
                if (time() > $begin && time() < $end) $inDuty = true;
            }
            if ($inDuty) {
                //值班分配
                $redisKey = RedisConstant::TaskPrefix . $taskType . ':user:' . RedisConstant::TaskAssignDuty . $taskSign;
                $assignMode = 8;
            }
        }
        list($code, $data) = getRedisData($assignMode, $redisKey);
        if (!$code) {
            $resultData['result'] = 2;
            $resultData['sendList'] = ['sleepTime' => 5 * 60];
            return $resultData;
        } else {
            if (!$data) {
                $resultData['result'] = 2;
                $resultData['sendList'] = ['sleepTime' => 5];
                return $resultData;
            }
            $resultData['result'] = 1;
            $resultData['userInfo'] = $data;
            $resultData['assign'] = ['mode' => $assignMode, 'skip_minute' => $setting['assignNotViewed'] ? $setting['viewWaitingTime'] : 0];
            return $resultData;
        }
    }
}